#include "vkshaderparser.h"
#include <iostream>
#include <fstream>
#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>
#include <glslang/Include/ResourceLimits.h>
#include "vkinfer/core/logger.h"


namespace vkinfer
{
	static std::shared_ptr<VkShaderParser> parser = nullptr;

	std::shared_ptr<VkShaderParser> VkShaderParser::get_instance(VkDevice device)
	{
		if (!parser)
			parser = std::make_shared<VkShaderParser>(device);
		return parser;
	}

	VkShaderParser::VkShaderParser(VkDevice device)
	{
		this->device = device;

#if 0
		std::cout << glslang::GetEsslVersionString() << std::endl;
		std::cout << glslang::GetGlslVersionString() << std::endl;

		glslang::InitializeProcess();
#endif
	}

	VkShaderParser::~VkShaderParser()
	{
#if 0
		glslang::FinalizeProcess();
#endif
	}

	VkShaderModule VkShaderParser::compile(const std::string& glsl_path)
	{
		// load shader source
		std::vector<char> shader_source = read_file(glsl_path);
		const char* shader_source_str = shader_source.data();

#if 0
		// convert to spirv
		EShLanguage stage = EShLangCompute;
		glslang::TShader shader(stage);
		shader.setStrings(&shader_source_str, 1);
		shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
		shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_3);
		shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
		TBuiltInResource buildinresource;
		EProfile noprofile;
		if (!shader.parse(&buildinresource, 100, ENoProfile, false, false, EShMsgDefault))
		{
			VK_ERROR(shader.getInfoLog());
			return VK_NULL_HANDLE;
		}

		glslang::TProgram program;
		program.addShader(&shader);
		if (!program.link(EShMsgDefault))
		{
			VK_ERROR(program.getInfoLog());
			return VK_NULL_HANDLE;
		}

		const auto intermediate = program.getIntermediate(stage);
		std::vector<uint32_t> spirv_code;
		glslang::GlslangToSpv(*intermediate, spirv_code);

		// create shader module
		VkShaderModuleCreateInfo shaderModuleCreateInfo = {};
		shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
		shaderModuleCreateInfo.codeSize = spirv_code.size() * sizeof(unsigned int);
		shaderModuleCreateInfo.pCode = reinterpret_cast<const uint32_t*>(spirv_code.data());

		VkShaderModule shader_module;
		auto result = vkCreateShaderModule(device, &shaderModuleCreateInfo, nullptr, &shader_module);
		if (result != VK_SUCCESS)
		{
			VK_ERROR("failed to compile shader: %s\n", glsl_path.c_str());
			return VK_NULL_HANDLE;
		}
		return shader_module;
#else
		return VK_NULL_HANDLE;
#endif
	}

	std::vector<char> VkShaderParser::read_file(const std::string& filepath)
	{
		std::ifstream file(filepath, std::ios::binary | std::ios::ate);
		if (!file.is_open())
			throw std::runtime_error("failed to open file");
		size_t filesize = file.tellg();
		std::vector<char> filebuffer(filesize + 1);
		file.seekg(0);
		file.read(filebuffer.data(), filesize);
		filebuffer[filesize] = 0;
		file.close();
		return filebuffer;
	}

	VkShaderModule compile(VkDevice device, const std::string& glsl_path)
	{
		return VkShaderParser::get_instance(device)->compile(glsl_path);
	}
}
